#include "Applejack/camera.hpp"
#include "Applejack/world.hpp"
#include "Applejack/body.hpp"

using namespace Shiny;
using namespace Applejack;

View::View(World& world) : Widgets::Container(), _world(world)
{
  const sf::Texture* landscape = world.GetLandscape();
  
  this->SetView(_view);
  if (landscape)
  {
    _landscape.SetTexture(*landscape);
    _landscapeTexSize = sf::Vector2f(landscape->GetWidth(), landscape->GetHeight());
  }
}

void View::SetSize(const sf::Vector2f& size)
{
  _view.SetSize(size);
  Container::SetSize(size);
}

void View::RenderSelf(void)
{
  b2Body* b2body = _world.GetBodyList();
  Refresh();
  DrawLandscape();
  while (b2body)
  {
    Body* body = reinterpret_cast<Body*>(b2body->GetUserData());

    if (body && _boundingBox.Intersects(body->Sprite().GetGlobalBounds()))
      this->Draw(body->Sprite());
    b2body = b2body->GetNext();
  }
}

void View::Refresh(void)
{
  sf::Vector2f             halfSize  = _view.GetSize();
  sf::Vector2f             cameraPos = _view.GetCenter();

  _boundingBox.Width     = halfSize.x;
  _boundingBox.Height    = halfSize.y;
  halfSize.x            /= 2;
  halfSize.y            /= 2;
  if (_sync)
    RefreshSync(halfSize, cameraPos);
  else
  {
    _boundingBox.Left    = cameraPos.x - halfSize.x;
    _boundingBox.Top     = cameraPos.y - halfSize.y;
  }
}

void View::RefreshSync(sf::Vector2f halfSize, sf::Vector2f cameraPos)
{
  const sf::Sprite&      sprite    = _sync->Sprite();
  sf::Vector2f           worldSize(_world.GetSize());
  sf::Vector2f           position  = sprite.GetPosition();
  sf::IntRect            rect      = sprite.GetTextureRect();

  position.x          += rect.Width  / 2;
  position.y          += rect.Height / 2;
  cameraPos.x         += (position.x - cameraPos.x) / _cameraSpeed;
  cameraPos.y         += (position.y - cameraPos.y) / _cameraSpeed;
  cameraPos.x         += _decalage.x;
  cameraPos.y         += _decalage.y;
  if      (cameraPos.x + halfSize.x > worldSize.x) { cameraPos.x = worldSize.x - halfSize.x; }
  else if (cameraPos.x < halfSize.x)               { cameraPos.x = halfSize.x;               }
  if      (cameraPos.y + halfSize.y > worldSize.y) { cameraPos.y = worldSize.y - halfSize.y; }
  else if (cameraPos.y < halfSize.y)               { cameraPos.y = halfSize.y;               }
  _boundingBox.Left    = cameraPos.x - halfSize.x;
  _boundingBox.Top     = cameraPos.y - halfSize.y;
  _view.SetCenter(cameraPos);
  this->SetView(_view);
}

#define LANDSCAPE_ADJUST(a, b, c, d) (a * ((b - d) / (c - d)))

void View::DrawLandscape(void)
{
  sf::Vector2f position = _view.GetCenter();
  sf::Vector2f size     = _view.GetSize();

  position.x -= (size.x / 2);
  position.y -= (size.y / 2);
  _landscape.SetPosition(position);
  
  sf::Vector2f worldSize = _world.GetSize();
  sf::Vector2f texSize   = _landscapeTexSize;
  sf::IntRect  toPrint;

  toPrint.Height = size.y;
  toPrint.Width  = size.x;
  toPrint.Left = LANDSCAPE_ADJUST(position.x, texSize.x, worldSize.x, size.x);
  toPrint.Top  = LANDSCAPE_ADJUST(position.y, texSize.y, worldSize.y, size.y);
  _landscape.SetTextureRect(toPrint);
  this->Draw(_landscape);
}

void View::SyncWithBody(const Body* body, int speed, sf::Vector2f decalage)
{
  _decalage    = decalage;
  _sync        = body;
  _cameraSpeed = speed;
}
